#include "Node.h"
#include <stdio.h>
#include <fstream>
#include <iostream>
#include "SystemCalls.h"

Node::Node(int id, int mailboxsize, int size, int* neighbours):Thread(update, (void*)this)
{
//cout<<"constructing Node ";
inserting=false;
 message_queue=new BoundedMailBox(mailboxsize);
 ID=id;
 //cout<< ID << ". the size of the node is ";
 _size=size;
//cout<< _size << "\n";
 
	


current_RT = new RT(_size); 
 //cout<< "passed this point";	
 
  
  

 //initialize the RTs array with all empty RTs.
 RTs_array= new RT[_size]; //??? is this the way?
 
 
neighbours_list=new int[size];  
//initialize the neighbours list with the neighbours.


for (int i=0 ; i<size; i++) {		
	neighbours_list[i]=neighbours[i];
	}
	//cout<<"\n";
 
 //cout<< "finished constructing Node " << ID << "\n";
}

Node::~Node()
{
	delete current_RT;	
	delete RTs_array;
	delete message_queue;
	delete neighbours_list;
}


void* Node::update(void* arg)
{

  //cout<< "\n starting update function for node " << "\n";
  
  
  Node* temp = (Node*)arg; 
  SystemCalls* system = SystemCalls::getInstance();
  RT* internal_T;
  Message* m = new Message(2,temp->getID(),0,internal_T);
  Message* n;
  int i,j,y,entry_id=0,entry_f=0,entry_dis=0,index,send_result;
  RT q;
  bool found;
  internal_T = new RT(temp->getsize());
  
while (true) { 
//cout<< "\n starting while true for node "<< temp->getID() << "\n";

  //1. for every message m currently awaiting in the node-thread's mailbox, process m.
  while (system->numofmessages(temp->getID()) != 0) {
  
  	//n = temp->message_queue->Extract();
	n=system->Receive(temp->getID());
	temp->process(n);
	}
  
  
  
  //2. A new internal_T is constructed including:		
  
  
  for (i=0 ; i< internal_T->size() ; i++) {
  	
  	internal_T->insert(i,1,i);	
	internal_T->insert(i,2,-1);
	internal_T->insert(i,3,-1);
	}
	
	
	//a. An entry {p,0,p}.
  internal_T->insert(temp->getID(),1,temp->getID());
  internal_T->insert(temp->getID(),2,0);
  internal_T->insert(temp->getID(),3,temp->getID());


 


// b. For every entry e from each RT_q in RTs_array , a new entry is added to
//internal_T with fields: 
// e.id, e.dis+1, q
  
  for (i=0 ; i<temp->getsize() ; i++) {
  	
	if (temp->get_RTs_array(i).size() != 0) {	

	//cout<<q->size()<<endl;
	q=temp->get_RTs_array(i);
	//q.printRT();
	for (j=0; j<q.size(); j++) {
		entry_id = q.get(j).ID;
		entry_dis = q.get(j).dis+1;
		entry_f = i;
	
		
	//cout<< entry_id <<"\n";
		//3. Every entry in internal_T whose dis field is not minimum with respect to all entries with the same id
		// field is extracted from internal_T. if there are more than one entry with a minimal value in their dis
		// field, only the one with the lowest from field stays and the others are eliminated.
		//cout<< internal_T->get(entry_id).dis <<"\n";
		
		if ((entry_dis!=0) && ((entry_dis < internal_T->get(entry_id).dis)  || (internal_T->get(entry_id).dis == -1))) {
			//cout<< "inside if, passed condition\n";
			
			internal_T->insert(entry_id,2,entry_dis);
			internal_T->insert(entry_id,3,entry_f);
		}
	}	
	}
  }
  
 
  
   
  //4. find the smallest natural number which does not appear in any 'dis' field in internal_T and name this number 'y'.
  int y_finder[internal_T->size()+1];
  for (i=0 ; i< internal_T->size()+1 ; i++) {
  	y_finder[i] = 0;
	}
  for (i=0 ; i<internal_T->size() ; i++) {
  	index = internal_T->get(i).dis;
	if (index != -1) y_finder[index]=1;
	}
  found=false;	
  i=0;
  while (! found) {
  	if (y_finder[i] == 0) {
		y=i;
		found=true;
	}
  	i++;
  }
  // end of finding y.

  
  
  //5. any entry in internal_T with a dis field larger than y is eliminated (this will be used to detect dead nodes as
  // will be explained in tirgul)
  for (i=0; i<internal_T->size() ; i++) {
  	if (internal_T->get(i).dis > y) {
		internal_T->insert(i,2,-1);
		internal_T->insert(i,3,-1);
	}
  }
  
  

  
  
  //6. current_RT is replaced by internal_T
  temp->current_RT=internal_T;
  
  
 // cout<< "the rt table of node " << temp->getID() << " is: \n" ;
 // temp->current_RT->printRT();
  

  
  //7. send messages containing current_RT to all neighbours. the message sending system call returns 0 if the neighbourinserting
  // is alive and -1 if the neighbour is not. when a node-thread finds out that its neighbour is not alive, it eliminates
  // that neighbours RT from the RT's array.
      
  
  
 m->setRT(temp->current_RT);
   

  for (i=0; i<temp->getsize(); i++) {
  	
  	if (temp->get_neighbours_list(i)==1) {
		
		Message* temp_m=new Message(m);
		temp_m->set_to(i);
  		send_result = temp->send(i,temp_m);
		
		if (send_result == -1) {
			
			temp->kill_RTs_array(i);
			
			}
	}
  } 
  

  //8. sleep 1 second
  sleep(1);
  
	}
delete internal_T;
delete m;
};

int Node::send(int dest, Message* m) 
{
//cout<< this->getID()<< " sent message to " << m->to() <<"\n";
SystemCalls* system = SystemCalls::getInstance();
inserting=true;
int result= system->Send(dest,m);
inserting=false;
return result;

}

void Node::process(Message* m)
{
 //cout<< this->getID() << "\n inside process message "  << "\n";
// 1. if m contains an RT of some neighbor q, then place this RT_q in the RTs_array. If the RTs_array already contains an
//older version of RT_q then replace it. 
	Message* temp_m=new Message(m);
	temp_m->set_to(-1);
	
  if (m->type()==2) {  	
       //cout<< this->getID() << "\n inside type 2 message "  << "\n";
	RTs_array[m->from()] = *m->getRT();
	//cout<< m->to() << " recieved a message with rt from " << m->from()<< "\n";
	// RTs_array[m->from()].printRT();
  }
  
  // 2. if m is a text message, then 
  if (m->type()==1) {
  	//cout<< this->getID() << "\n inside type 1 message " << "\n";
	// a. if the text message is for me, then send a message to the shell to print.
	
	
	
  	if (m->dest() == ID) 
	{	
	
	send(-1,temp_m);
	
	}
	
	// b. else the message is for some other node-thread k. look at the current_RT[k].f field. if there is such an
	// entry, denote its value by l. if l exists, send the text message to l and send a message to the shell to print.
	
	else if (current_RT->get(m->dest()).f != -1) {		
		int l= current_RT->get(m->dest()).f;		
		m->set_to(l);
		send(l,m);
		
		temp_m->set_dest(l);
		temp_m->set_type(2);
		temp_m->set_from(ID);
		send(-1,temp_m);
		
		}
		// if l does not exist, discard m and send the shell a message to print.
		else { 
		temp_m->set_type(3);
		temp_m->set_from(ID);
		send(-1,temp_m);
		
		}
	}  
	//cout<< "\n finished processing message for node "  << this->getID() <<"\n";
	
  if (m->type()==3) {
  	temp_m->setRT(current_RT);
	temp_m->set_type(4);
  	send(-1,temp_m);
	
  }
	
}

bool Node::getIsInserting()
{
	return inserting;
}

int Node::getsize()
{
	return _size;
}

int Node::getID()
{
	return ID;
}

RT* Node::get_current_RT()
{
	return current_RT;
}	

RT Node::get_RTs_array(int index)
{
	return RTs_array[index];
}

void Node::kill_RTs_array(int index)
{
	
	RT* empty = new RT(0);
	
	RTs_array[index]=*empty;
	
}

int Node::getMailBoxSize()
{
	return message_queue->getSize();
}

BoundedMailBox* Node::getMailBox()
{
	return message_queue;
}

int Node::get_neighbours_list(int i) {   
   return neighbours_list[i];
}

int Node::getMailBoxMaxSize()
{
	return message_queue->getMaxSize();
}
